package com.lzy.sso.cas.controller;

import com.lzy.sso.cas.pojo.Message;
import com.lzy.sso.cas.pojo.Subject;
import com.lzy.sso.cas.principle.Principle;
import com.lzy.sso.cas.service.AuthenticationService;
import com.lzy.sso.cas.property.AuthenticationProperties;
import com.lzy.sso.cas.utils.CookieUtils;
import com.lzy.sso.cas.utils.WebUrlUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;

/**
 * @Author: lzy
 * @Date: 2022/4/25 16:34
 * @Description: 第三方认证
 * 加@controller为使用starter时注册进MappingRegistry
 */

@Slf4j
@Controller
public class AuthenticationController implements InitializingBean {
    //占位符
    public static final String PLACEHOLDER="place-holder";

    @Autowired
    private AuthenticationService authenticationService;

    //认证
    @GetMapping(PLACEHOLDER)
    public String auth(@RequestParam("pageUrl") String pageUrl,
                       @RequestParam("showPage") String showPage,
                       HttpServletRequest request,HttpServletResponse response){
        String token = CookieUtils.getCookieValue("token",request.getCookies());
        if(token!=null&&authenticationService.auth(token)){
            pageUrl= WebUrlUtils.appendAttribute(pageUrl,"token",token);
            pageUrl+="&showPage="+showPage;

            log.info("验证成功...");
        } else {
            String page=properties.getLoginPage();
            pageUrl=page+"?pageUrl="+pageUrl+"&showPage="+showPage;
        }

        return "redirect:"+pageUrl;
    }

    //置换信息
    @ResponseBody
    @PostMapping(PLACEHOLDER)
    public Message<Map<String,Object>> info(@RequestParam("token") String token){
        if(authenticationService.auth(token)){
            return new Message<>(200,"success",authenticationService.getInfo(token));
        }

        return new Message<>(500,"failed",null);
    }

    //登录
    @ResponseBody
    @PostMapping(PLACEHOLDER)
    public Message<String> login(@RequestParam("pageUrl") String pageUrl,
                                 @RequestParam(value = "showPage",required = false) String showPage,
                                 @RequestBody Subject subject,
                                 HttpServletResponse response ,HttpServletRequest request){
        Message<String> msg=new Message<String>();

        Principle principle=null;
        if((principle=authenticationService.login(subject))!=null){
            msg.setCode(200);
            msg.setMsg("success");
            String token=authenticationService.processToken(request,response,principle);
            showPage=(showPage==null)?properties.getWelcomePage():showPage;
            msg.setVal(token+";"+pageUrl+";"+showPage);

            log.info("登录成功...");
        }else{
            msg.setCode(400);
            msg.setMsg("error");
            msg.setVal(null);
        }

        return msg;
    }

    //注销
    @ResponseBody
    @PostMapping(PLACEHOLDER)
    public Message<String> logout(HttpServletResponse response ,HttpServletRequest request){
        Message<String> msg=new Message<String>();

        if(authenticationService.isActive(request,response)){
            authenticationService.logout(request,response);
            msg.setCode(200);
            msg.setVal("success");
        } else {
            msg.setCode(500);
            msg.setMsg("当前不在登录状态!");
        }

        return msg;
    }

    @Autowired
    private AuthenticationProperties properties;

    /**
     * 动态改变认证以及登录的路由
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        Method[] methods = this.getClass().getDeclaredMethods();
        if(methods!=null) {
            for (Method method : methods){
                if("auth".equals(method.getName())||"info".equals(method.getName())){
                    Object annotation = "auth".equals(method.getName())?method.getAnnotation(GetMapping.class):method.getAnnotation(PostMapping.class);
                    setAnnotationValue(annotation,new String[]{properties.getAuthUri()});
                } else if ("login".equals(method.getName())){
                    PostMapping annotation = method.getAnnotation(PostMapping.class);
                    setAnnotationValue(annotation,new String[]{properties.getLoginUri()});
                } else if ("logout".equals(method.getName())){
                    PostMapping annotation = method.getAnnotation(PostMapping.class);
                    setAnnotationValue(annotation,new String[]{properties.getLogoutUri()});
                }
            }
        }

        StringBuilder stringBuilder=new StringBuilder();
        stringBuilder.append("\t--- 登录路由: GET   "+properties.getLoginUri()+"\n");
        stringBuilder.append("\t--- 认证路由: GET   "+properties.getAuthUri()+"\n");
        stringBuilder.append("\t--- 信息获取: POST  "+properties.getAuthUri()+"\n");
        stringBuilder.append("\t--- 注销路由: POST  "+properties.getLogoutUri()+"\n");

        log.info("已初始化AuthenticationController...\n{}",stringBuilder.toString());
    }

    /**
     * 设置annotation的值
     * @param annotation
     * @param value
     * @throws Exception
     */
    private void setAnnotationValue(Object annotation,Object value) throws Exception {
        InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation);

        Field field=invocationHandler.getClass().getDeclaredField("memberValues");
        field.setAccessible(true);

        Map<Object,Object> map = (Map<Object, Object>) field.get(invocationHandler);
        map.put("value",value);
    }
}
